package com.buddy.learn.spring.framework.context;

import com.buddy.learn.spring.framework.aop.BAopProxy;
import com.buddy.learn.spring.framework.aop.BCglibAopProxy;
import com.buddy.learn.spring.framework.aop.BJdkDynamicAopProxy;
import com.buddy.learn.spring.framework.aop.config.BAopConfig;
import com.buddy.learn.spring.framework.aop.support.BAdvisedSupport;
import com.buddy.learn.spring.framework.beans.BBeanFactory;
import com.buddy.learn.spring.framework.beans.BBeanWrapper;
import com.buddy.learn.spring.framework.beans.config.BBeanDefinition;
import com.buddy.learn.spring.framework.beans.config.BBeanPostProcessor;
import com.buddy.learn.spring.framework.beans.support.BBeanDefinitionReader;
import com.buddy.learn.spring.framework.beans.support.BDefaultListableFactory;
import com.buddy.learn.spring.mvc.annotation.BAutowired;
import com.buddy.learn.spring.mvc.annotation.BController;
import com.buddy.learn.spring.mvc.annotation.BService;

import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;

/**
 * IOC DI MVC AOP
 */
public class BApplicationContext extends BDefaultListableFactory implements BBeanFactory {


    private String[] configLocations;
    private BBeanDefinitionReader reader;

    //IOC容器，单例的IOC容器缓存
    private Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

    //通用过的IOC容器
    private Map<String, BBeanWrapper> factoryBeanFactoryCache = new ConcurrentHashMap<>(256);

    public BApplicationContext(String... configLocations) {
        this.configLocations = configLocations;
    }


    /**
     * 依赖注入DI的入口
     *
     * @param beanName
     * @return
     */
    @Override
    public Object getBean(String beanName) throws Exception {
        //为什么要分成两步？避免循环依赖
//        BBeanDefinition beanDefinition = new BBeanDefinition();
//        beanDefinition.setFactoryBeanName(beanName);
        BBeanDefinition beanDefinition = this.beanDefinitionMap.get(beanName);
        //初始化x
        Object instance = instantiateBean(beanName, beanDefinition);
        //Bean实例化的前置和后置处理通知
//        new BeanPostProcessor();
        BBeanPostProcessor beanPostProcessor = new BBeanPostProcessor();

        //前置处理
        //TODO 要使用工厂+策略模式
        beanPostProcessor.postProcessBeforeInitialization(instance, beanName);
        //1.初始化，实例化Bean
        BBeanWrapper beanWrapper = new BBeanWrapper(instance);
        //前置处理
        beanPostProcessor.postProcessBeforeInitialization(beanWrapper.getWrappedInstance(), beanName);
        //2.获取到BeanWrapper之后，要保存到IOC容器中
        //BeanName是否重复。。。。
//        if (this.factoryBeanFactoryCache.containsKey(beanName)){
//            throw new Exception("The "+beanName+" isExists!");
//        }
        this.factoryBeanFactoryCache.put(beanName, beanWrapper);

        //TODO 要使用工厂+策略模式
        beanPostProcessor.postProcessAfterInitialization(instance, beanName);
        //3.注入
        populateBean(beanName, new BBeanDefinition(), beanWrapper);


        return this.factoryBeanFactoryCache.get(beanName).getWrappedInstance();
    }

    public Object getBean(Class<?> beanClass) throws Exception {
        return getBean(beanClass.getName());
    }

    private void populateBean(String beanName, BBeanDefinition bBeanDefinition, BBeanWrapper bBeanWrapper) {
        Object instance = bBeanWrapper.getWrappedInstance();
        Class<?> clazz = bBeanWrapper.getWrappedClass();
        //判断只有加了注解的class才执行依赖注入

        if (!(clazz.isAnnotationPresent(BController.class) || clazz.isAnnotationPresent(BService.class))) {
            return;
        }
        //获得所有字段
        Field[] fields = clazz.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(BAutowired.class))
                continue;

            BAutowired autowired = field.getAnnotation(BAutowired.class);
            String autowiredBeanName = autowired.value().trim();
            if ("".equals(autowiredBeanName)) {
                autowiredBeanName = field.getType().getName();
            }

            //设置强制访问
            field.setAccessible(true);
            try {
                //TODO 为什么会为null
                if (this.factoryBeanFactoryCache.get(autowiredBeanName) == null) {
                    continue;
                }
                field.set(instance, this.factoryBeanFactoryCache.get(autowiredBeanName).getWrappedInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

    }

    private BBeanWrapper instantiateBean(String beanName, BBeanDefinition bBeanDefinition) {
        //1.获取要实例化的对象的类名
        String className = bBeanDefinition.getBeanClassName();
        //2.反射实例化，得到一个对象
        Object instance = null;
        try {
            //假设默认单例
            if (this.singletonObjects.containsKey(className)) {
                instance = this.singletonObjects.get(className);
            } else {

                Class<?> clazz = Class.forName(className);
                instance = clazz.newInstance();
                BAdvisedSupport config = instantionAopConfig(bBeanDefinition);
                config.setTargetClass(clazz);
                config.setTarget(instance);

                //如果符合pointCut规则，创建代理对象
                if (config.pointCutMatch()) {
                    instance = createProxy(config).getProxy();
//                }else{
                    //使用原生对象
                }

//                Class<?> clazz = Class.forName(className);
//                instance = clazz.newInstance();
                this.singletonObjects.put(className, instance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        //3.将对象封装到BeanWrapper
        // DefaultSingletonBeanRegistry
        // private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
        //AbstractAutowireCapableBeanFactory
        //private final Map<String, BeanWrapper> factoryBeanInstanceCache = new ConcurrentHashMap<>(16);
        BBeanWrapper beanWrapper = new BBeanWrapper(instance);
        //4.将BeanWrapper存到IOC容器中
        return beanWrapper;
    }

    //选择代理策略
    private BAopProxy createProxy(BAdvisedSupport config) {
        Class targetClass = config.getTargetClass();
        if (targetClass.getInterfaces().length > 0){
            //JDK动态代理基于接口
            return new BJdkDynamicAopProxy(config);
        }
        return new BCglibAopProxy(config);
    }

    //初始化AOP配置
    private BAdvisedSupport instantionAopConfig(BBeanDefinition bBeanDefinition) {
        BAopConfig config = new BAopConfig();
        config.setPointCut(this.reader.getConfig().getProperty("pointCut"));
        config.setAspectClass(this.reader.getConfig().getProperty("aspectClass"));
        config.setAspectBefore(this.reader.getConfig().getProperty("aspectBefore"));
        config.setAsepctAfter(this.reader.getConfig().getProperty("aspectAfter"));
        config.setAspectAfterThrow(this.reader.getConfig().getProperty("aspectThrow"));
        config.setAspectAfterThrowingName(this.reader.getConfig().getProperty("aspectThrowingName"));
        return new BAdvisedSupport(config);
    }

    /**
     * 容器初始化的起点
     */
    @Override
    public void refresh() {
        //1.定位，定位配置文件
        reader = new BBeanDefinitionReader(configLocations);
        //2.加载配置文件，扫描相关的类，把他们封装到BeanDefinition
        List<BBeanDefinition> beanDefinitions = reader.loadBeanDefinitions();
        //3.注册，把配置信息放到容器里面（伪IOC容器，真真的IOC容器是Wapper）
        doRegisterBeanDefinition(beanDefinitions);
        //4.把不是延时加载的类，提前初始化
        doAutowire();

    }

    /**
     * 只处理非延迟加载的情况
     */
    private void doAutowire() {
        for (Map.Entry<String, BBeanDefinition> beanDefinitionEntry : super.beanDefinitionMap.entrySet()) {
            String beanName = beanDefinitionEntry.getKey();
            if (!beanDefinitionEntry.getValue().isLazyInit()) {
                //如果不是延迟加载
                try {
                    getBean(beanName);
                } catch (Exception e) {
                    e.printStackTrace();
                }

            }

        }

    }

    private void doRegisterBeanDefinition(List<BBeanDefinition> beanDefinitions) {
        for (BBeanDefinition beanDefinition : beanDefinitions) {
            super.beanDefinitionMap.put(beanDefinition.getFactoryBeanName(), beanDefinition);
        }
    }

    public String[] getBeanDefinitionNames() {
        return this.beanDefinitionMap.keySet().toArray(new String[this.beanDefinitionMap.size()]);
    }

    public int getBeanDenfinitionCount() {
        return this.beanDefinitionMap.size();
    }

    public Properties getConfig() {
        return this.reader.getConfig();
    }
}
